import sys
sys.path.append('/home/sunrise/python_project1/code') 

import os
import cv2
import numpy as np
import time
import json
import multiprocessing
import queue
from multiprocessing_task import *


# --- 配置 ---
ping_interval = 0.01 # 串口发送频率，50ms 这个频率非常高

RUNNING_FLAG = multiprocessing.Event() # 所有任务运行标志
RUNNING_FLAG.set()  # 设置为True，表示所有任务正在运行


ENABLE_WEB_DISPLAY = multiprocessing.Value('b', True)      # 显示网页 :  True开启  False关闭
ENABLE_YOLOV8_DETECT = multiprocessing.Value('b', False)    # yolov8图像检测:  True: 启用, False: 禁用
ENABLE_FIND_COUNTS = multiprocessing.Value('b',  True)      # 激光和轮廓检测:  True: 启用, False: 禁用

# 串口控制:  True: 启用, False: 禁用
ENABLE_UART1 = multiprocessing.Value('b', True)
ENABLE_UART2 = multiprocessing.Value('b', True)
ENABLE_UART3 = multiprocessing.Value('b', True)
ENABLE_UART6 = multiprocessing.Value('b', True)
#{"yolov8_detect":0,"web_display":1}  # 串口发送类似的数据控制任务的启用状态  0: 禁用, 1: 启用

CONTROL_FLAGS_MAP = {
    "web_display": ENABLE_WEB_DISPLAY,
    "yolov8_detect": ENABLE_YOLOV8_DETECT,
    "find_counts": ENABLE_FIND_COUNTS,
    "uart1": ENABLE_UART1,
    "uart2": ENABLE_UART2,
    "uart3": ENABLE_UART3,
    "uart6": ENABLE_UART6
}


# 串口控制: 队列 
serial1_write = multiprocessing.Queue()
serial1_read  = multiprocessing.Queue()

serial2_write = multiprocessing.Queue()
serial2_read  = multiprocessing.Queue()

serial3_write = multiprocessing.Queue()
serial3_read  = multiprocessing.Queue()

serial6_write = multiprocessing.Queue()
serial6_read = multiprocessing.Queue()

image_to_web_queue = multiprocessing.Queue(maxsize=5)  # web显示队列
frame1_queue = multiprocessing.Queue() #  创建一个队列用于从摄像头进程接收帧
data_result_queue = multiprocessing.Queue(maxsize=10) # 用于发送识别结果给下位机


# 串口配置列表 "/dev/ttyS1" "/dev/ttyS2" "/dev/ttyS3" "/dev/ttyS6"
ports = [
    (ENABLE_UART1, "/dev/ttyS1", 115200, serial1_write, serial1_read),
    (ENABLE_UART2, "/dev/ttyS2", 230400, serial2_write, serial2_read),
    (ENABLE_UART3, "/dev/ttyS3", 230400, serial3_write, serial3_read),
    (ENABLE_UART6, "/dev/ttyS6", 230400, serial6_write, serial6_read),
]


# --- 主进程逻辑 ---
def main_process():

    print("[主进程] 应用程序正在启动...")
    processes = []

    #1. 发现摄像头设备
    # v4l2-ctl --device=/dev/video0 --list-formats-ext # 列出摄像头支持的分辨率和帧数
    p_video1_frame = multiprocessing.Process(target=camera_reader_process,
                                            args=(RUNNING_FLAG, frame1_queue,'cap_main',{'width': 640, 'height': 480, 'fps': 30}),
                                            daemon=True)

    processes.append({"name": "video1_frame", "process": p_video1_frame})
    p_video1_frame.start()

    # 2. 启动串口进程
    p_serial = multiprocessing.Process(target=serial_process_task,
                                       args=(RUNNING_FLAG, ports),
                                       daemon=True)
    processes.append({"name": "Serial", "process": p_serial})
    p_serial.start()

    # 3. 启动图像处理进程(如果找到摄像头)
    p_image_proc = multiprocessing.Process(target=image_processing_process_task,
                                        args=(RUNNING_FLAG, ENABLE_WEB_DISPLAY, ENABLE_YOLOV8_DETECT, ENABLE_FIND_COUNTS,
                                                frame1_queue, image_to_web_queue,data_result_queue),
                                        daemon=True)
    processes.append({"name": "ImageProcessing", "process": p_image_proc})
    p_image_proc.start()

    # 4. 启动网页显示进程(始终启动,如果显示被禁用则空闲)
    p_web_display = multiprocessing.Process(target=web_display_process_task,
                                            args=(RUNNING_FLAG, ENABLE_WEB_DISPLAY, image_to_web_queue),
                                            daemon=True)
    processes.append({"name": "WebDisplay", "process": p_web_display})
    p_web_display.start()

    # --- 用于定时发送串口数据的变量 ---
    last_data_send_time = time.time()

    try:
        while RUNNING_FLAG.is_set():
            current_time = time.time()
            something_was_done_this_cycle = False # 标记本轮循环是否有活动

             # --- 1. 尝试从 data_result_queue 获取并发送数据 (受 ping_interval 控制频率) ---
            if current_time - last_data_send_time >= ping_interval:
                data_to_send = None
                try:
                    # Try to get data without blocking indefinitely, but allow some time
                    data_to_send = data_result_queue.get(block=False,timeout=0.01) # Small timeout
                except queue.Empty:
                    print("[主进程] data_result_queue is empty. No data to send this interval.")

                if data_to_send:
                    try:
                        # 解包坐标元组
                        if isinstance(data_to_send, tuple) and len(data_to_send) == 2:

                            cx, cy = data_to_send
                            # cx,cy = 30,30
                            pos_data = {"Pos":f"{cx},{cy}"}  # 动态构造坐标字符串

                            #print(f"[主进程] 发送坐标: {pos_data}")
                        else:

                            print(f"无效坐标格式: {type(data_to_send)}")
                            return
                        # #y = 1234
                        # # 构造多个字典数据
                        # extra_data = {
                        #     # "ExtraData":f"{y}",
                        #     "yolov8": 0
                        #     # "web_display": 1
                        # }

                        # # 合并字典（如果需要一次性发送所有字段在同一个 JSON 对象中）
                        # merged_data = {**pos_data, **extra_data}  # 合并两个字典
                        #转换为 JSON 字符串并添加结束符（如有需要）
                        json_str = json.dumps(pos_data, separators=(',', ':'), ensure_ascii=False) + "\r\n"

                        # 发送到串口
                        serial1_write.put(json_str, block=False, timeout=0.001)
                        something_was_done_this_cycle = True
                    except Exception as e:
                        print(f"坐标发送失败: {str(e)}")
                    except queue.Full:
                        print("[主进程] 串口写入队列已满, data_result_queue 消息丢弃。")
                    except TypeError as te:
                        print(f"[主进程] 无法序列化发送的数据 (TypeError): {te}. Data: {data_to_send}")
                    except Exception as e_send:
                        print(f"[主进程] 串口发送 data_result_queue 数据时出错: {type(e_send).__name__} - {e_send}")

                last_data_send_time = current_time # Reset timer whether data was sent or not, to maintain interval

            # --- 2. 处理来自串口任务的消息 (高优先级) ---
            try:
                raw_task_command_str = serial1_read.get(block=False)
                print(f"[主进程] 从串口收到原始数据: {raw_task_command_str}")
                something_was_done_this_cycle = True
                task_command_dict = None

                if isinstance(raw_task_command_str, str) and raw_task_command_str     .strip():
                    try:
                        task_command_dict = json.loads(raw_task_command_str)
                        print(f"[主进程] 解析后的任务字典: {task_command_dict}")
                    except json.JSONDecodeError as je:
                        # print(f"[主进程] JSON 解析错误: {je}, 原始数据: '{raw_task_command_str}'")
                        continue
                elif isinstance(raw_task_command_str, dict):
                    task_command_dict = raw_task_command_str
                else:
                    print(f"[主进程] 从队列收到的数据不是有效的 JSON 字符串或字典: {type(raw_task_command_str)}, 内容: '{raw_task_command_str}'")
                    continue

          
            except queue.Empty:
                pass
            except Exception as e:
                print(f"[主进程] 主循环处理串口任务时发生错误: {e}")

            if not something_was_done_this_cycle:
                time.sleep(0.001) # Reduced sleep time for higher responsiveness

    except KeyboardInterrupt:
        print("[主进程] 收到KeyboardInterrupt。正在关闭...")
    finally:
        print("[主进程] 正在启动关闭序列...")
        RUNNING_FLAG.clear()

        for p_info in processes:
            print(f"[主进程] 等待{p_info['name']}进程退出...")
            try:
                p_info['process'].join(timeout=3) # Shorter timeout
                if p_info['process'].is_alive():
                    print(f"[主进程] {p_info['name']}进程未正常退出,强制终止。")
                    p_info['process'].terminate()
                    p_info['process'].join(timeout=1)
            except Exception as e:
                print(f"[主进程] 加入/终止{p_info['name']}时出错: {e}")

        # Close queues
        queues_to_close = [
            serial1_write, serial1_read, serial2_write, serial2_read,
            serial3_write, serial3_read, image_to_web_queue,
            frame1_queue
        ]
        
        for q_obj in queues_to_close:
            try:
                q_obj.close()
                q_obj.join_thread()
                print(f"[主进程] 队列已关闭: {q_obj}")
            except Exception as e_q:
                print(f"[主进程] 关闭队列时出错: {e_q}")
        
        cv2.destroyAllWindows()
        print("[主进程] 应用程序已关闭。")


if __name__ == "__main__":
    multiprocessing.freeze_support() # Good practice for Windows, harmless on Linux
    main_process()